/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.interpreter.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author Laurent WOUTERS
 */
public abstract class JavaDatatype {
    protected String owlDatatype;
    protected String shortOWLDatatype;
    protected java.lang.Class javaType;
    protected java.lang.Class javaPrimitiveType;

    public String getOWLDatatype() { return owlDatatype; }
    public String getShortOWLDatatype() { return shortOWLDatatype; }
    public java.lang.Class getJavaType() { return javaType; }
    public java.lang.Class getJavaPrimitiveType() { return javaPrimitiveType; }
    public abstract String getToString(Object expression);
    public abstract Object getToValue(String expression);
    public abstract String serialize(String lexicalValue);
    public JavaDatatype(String owlDatatype, java.lang.Class javaType, java.lang.Class javaPrimitiveType) {
        this.owlDatatype = owlDatatype;
        String[] parts = owlDatatype.split("#");
        this.shortOWLDatatype = parts[parts.length-1];
        this.javaType = javaType;
        this.javaPrimitiveType = javaPrimitiveType;
    }

    private static class DatatypeByte extends JavaDatatype {
        public DatatypeByte(String owlDatatype) { super(owlDatatype, java.lang.Byte.class, java.lang.Byte.TYPE); }
        @Override public String getToString(Object expression) { return expression.toString(); }
        @Override public Object getToValue(String expression) { return java.lang.Byte.valueOf(expression ); }
        @Override public String serialize(String lexicalValue) { return "\"" + lexicalValue + "\"xsd:" + shortOWLDatatype; }
    }
    private static class DatatypeShort extends JavaDatatype {
        public DatatypeShort(String owlDatatype) { super(owlDatatype, java.lang.Short.class, java.lang.Short.TYPE); }
        @Override public String getToString(Object expression) { return expression.toString(); }
        @Override public Object getToValue(String expression) { return java.lang.Short.valueOf(expression); }
        @Override public String serialize(String lexicalValue) { return "\"" + lexicalValue + "\"xsd:" + shortOWLDatatype; }
    }
    private static class DatatypeInteger extends JavaDatatype {
        public DatatypeInteger(String owlDatatype) { super(owlDatatype, java.lang.Integer.class, java.lang.Integer.TYPE); }
        @Override public String getToString(Object expression) { return expression.toString(); }
        @Override public Object getToValue(String expression) { return java.lang.Integer.valueOf(expression); }
        @Override public String serialize(String lexicalValue) { return lexicalValue; }
    }
    private static class DatatypeLong extends JavaDatatype {
        public DatatypeLong(String owlDatatype) { super(owlDatatype, java.lang.Long.class, java.lang.Long.TYPE); }
        @Override public String getToString(Object expression) { return expression.toString(); }
        @Override public Object getToValue(String expression) { return java.lang.Long.valueOf(expression); }
        @Override public String serialize(String lexicalValue) { return "\"" + lexicalValue + "\"xsd:" + shortOWLDatatype; }
    }
    private static class DatatypeFloat extends JavaDatatype {
        public DatatypeFloat(String owlDatatype) { super(owlDatatype, java.lang.Float.class, java.lang.Float.TYPE); }
        @Override public String getToString(Object expression) { return expression.toString(); }
        @Override public Object getToValue(String expression) { return java.lang.Float.valueOf(expression); }
        @Override public String serialize(String lexicalValue) { return lexicalValue; }
    }
    private static class DatatypeDouble extends JavaDatatype {
        public DatatypeDouble(String owlDatatype) { super(owlDatatype, java.lang.Double.class, java.lang.Double.TYPE); }
        @Override public String getToString(Object expression) { return expression.toString(); }
        @Override public Object getToValue(String expression) { return java.lang.Double.valueOf(expression); }
        @Override public String serialize(String lexicalValue) { return "\"" + lexicalValue + "\"xsd:" + shortOWLDatatype; }
    }
    private static class DatatypeBoolean extends JavaDatatype {
        public DatatypeBoolean(String owlDatatype) { super(owlDatatype, java.lang.Boolean.class, java.lang.Boolean.TYPE); }
        @Override public String getToString(Object expression) { return expression.toString(); }
        @Override public Object getToValue(String expression) { return java.lang.Boolean.valueOf(expression); }
        @Override public String serialize(String lexicalValue) { return "\"" + lexicalValue + "\"xsd:" + shortOWLDatatype; }
    }
    private static class DatatypeString extends JavaDatatype {
        public DatatypeString(String owlDatatype) { super(owlDatatype, java.lang.String.class, null); }
        @Override public String getToString(Object expression) { return expression.toString(); }
        @Override public Object getToValue(String expression) { return expression; }
        @Override public String serialize(String lexicalValue) { return "\"" + lexicalValue + "\""; }
    }
    private static class DatatypeDate extends JavaDatatype {
        public DatatypeDate(String owlDatatype) { super(owlDatatype, Date.class, null); }
        @Override public String getToString(Object expression) { return java.text.SimpleDateFormat.getInstance().format(expression); }
        @Override public Object getToValue(String expression) {
            try { return java.text.SimpleDateFormat.getInstance().parse(expression); }
            catch (java.lang.Exception ex) { return null; }
        }
        @Override public String serialize(String lexicalValue) { return "\"" + lexicalValue + "\"xsd:" + shortOWLDatatype; }
    }

    private static final Map<String, JavaDatatype> mapIRIToDatatype;
    private static final Map<java.lang.Class, JavaDatatype> mapClassToDatatype;
    private static final Map<java.lang.Class, JavaDatatype> mapPrimitiveClassToDatatype;
    static {
        mapIRIToDatatype = new HashMap<String, JavaDatatype>();
        mapClassToDatatype = new HashMap<java.lang.Class, JavaDatatype>();
        mapPrimitiveClassToDatatype = new HashMap<java.lang.Class, JavaDatatype>();
        createNewDatatype(OWLDatatype.xsdByte, DatatypeByte.class, true);
        createNewDatatype(OWLDatatype.xsdUnsigedByte, DatatypeByte.class);
        createNewDatatype(OWLDatatype.xsdShort, DatatypeShort.class, true);
        createNewDatatype(OWLDatatype.xsdUnsignedShort, DatatypeShort.class);
        createNewDatatype(OWLDatatype.xsdInt, DatatypeInteger.class);
        createNewDatatype(OWLDatatype.xsdInteger, DatatypeInteger.class, true);
        createNewDatatype(OWLDatatype.xsdUnsignedInteger, DatatypeInteger.class);
        createNewDatatype(OWLDatatype.xsdNonNegativeInteger, DatatypeInteger.class);
        createNewDatatype(OWLDatatype.xsdNonPositiveinteger, DatatypeInteger.class);
        createNewDatatype(OWLDatatype.xsdPositiveInteger, DatatypeInteger.class);
        createNewDatatype(OWLDatatype.xsdNegativeInteger, DatatypeInteger.class);
        createNewDatatype(OWLDatatype.xsdLong, DatatypeLong.class, true);
        createNewDatatype(OWLDatatype.xsdUnsignedLong, DatatypeLong.class);
        createNewDatatype(OWLDatatype.xsdDouble, DatatypeDouble.class, true);
        createNewDatatype(OWLDatatype.xsdFloat, DatatypeFloat.class, true);
        createNewDatatype(OWLDatatype.xsdDecimal, DatatypeDouble.class);
        createNewDatatype(OWLDatatype.xsdBoolean, DatatypeBoolean.class, true);
        createNewDatatype(OWLDatatype.xsdString, DatatypeString.class, true);
        createNewDatatype(OWLDatatype.xsdDate, DatatypeDate.class);
        createNewDatatype(OWLDatatype.xsdDateTime, DatatypeDate.class, true);
        createNewDatatype(OWLDatatype.xsdDuration, DatatypeDate.class);
        createNewDatatype(OWLDatatype.xsdTime, DatatypeDate.class);

        createNewDatatype(OWLDatatype.owlReal, DatatypeDouble.class);
        createNewDatatype(OWLDatatype.owlRational, DatatypeDouble.class);
        createNewDatatype(OWLDatatype.rdfPlainLiteral, DatatypeString.class);
    }
    private static void createNewDatatype(String owlForm, java.lang.Class c) {
        createNewDatatype(owlForm, c, false);
    }
    private static void createNewDatatype(String owlForm, java.lang.Class c, boolean main) {
        try {
            java.lang.reflect.Constructor constructor = c.getConstructor(String.class);
            JavaDatatype javaDatatype = (JavaDatatype)constructor.newInstance(owlForm);
            mapIRIToDatatype.put(owlForm, javaDatatype);
            if (main) {
                mapClassToDatatype.put(javaDatatype.getJavaType(), javaDatatype);
                if (javaDatatype.getJavaPrimitiveType() != null)
                    mapPrimitiveClassToDatatype.put(javaDatatype.getJavaPrimitiveType(), javaDatatype);
            }
        }
        catch (java.lang.NoSuchMethodException ex) {}
        catch (java.lang.InstantiationException ex) {}
        catch (java.lang.IllegalAccessException ex) {}
        catch (java.lang.reflect.InvocationTargetException ex) {}
    }
    public static JavaDatatype get(String owlDatatype) { return mapIRIToDatatype.get(owlDatatype); }
    public static JavaDatatype get(java.lang.Class c) {
        JavaDatatype datatype =  mapClassToDatatype.get(c);
        if (datatype != null)
            return datatype;
        return mapPrimitiveClassToDatatype.get(c);
    }
}
